package cn.seaboot.excel.sax;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

/**
 * excel 处理上下文
 * <p>
 * 简单的指针指示器，更方便地观测正在写入的数据位置，
 * <p>
 * 使数据写入过程中，不必关注合并单元格带来的影响。
 * <p>
 * 不要在编辑区域添加空的单元格，避免逻辑出错（空的单元格用于占位）
 *
 * @author Mr.css
 * @version 2025-07-01 14:19
 */
public class ExcelContext {

    /**
     * 下一个要处理的行号、列号
     * <p>
     * 比如当前处理 [0, 1] 单元格, 则 rn = 0，cn = 2，
     * <p>
     * 下一个单元格，有可能没数据，也有可能是被合并的单元格，程序上获取当前值意义不大，
     */
    private int rn = 0, cn = 0;

    /**
     * 起始行号、列号
     * <p>
     * 数据不一定要从 [0, 0] 单元格开始写，可以任意取一个起始位置开始写入数据
     */
    private int startRow = 0, startCol = 0;


    /**
     * 当前正在处理的工作表
     */
    private Sheet sheet;

    /**
     * 当前正在处理的行
     */
    private Row row;

    /**
     * 当前正在处理的单元格
     */
    private Cell cell;

    /**
     * 跳转到下一行
     *
     * @return 新的一行对象
     */
    public Row nextLine() {
        Row ret = sheet.getRow(rn);
        if (ret == null) {
            ret = sheet.createRow(rn);
        }
        this.row = ret;

        this.rn++;
        this.cn = this.startCol;
        return ret;
    }

    /**
     * 下一个单元格
     *
     * @return 需要处理的单元格
     */
    public Cell nextCell() {
        while (true) {
            Cell ret = row.getCell(cn);
            if (ret == null) {
                // create new cell
                return this.cell = row.createCell(cn);
            } else {
                // ignore already exits cells, which maybe merged into other cells
                // 忽略已存在单元格：一种是此前已有数据，另一种是合并单元格时提前创建好的
                this.cn++;
            }
        }
    }

    /**
     * 合并单元格
     * <p>
     * 创建空的单元格，之后合并单元格。
     * <p>
     * 空的单元格没什么标志位，后续代码根据是否存在单元格进行判断，一定要注意代码逻辑
     * <p>
     * html 和 excel 的合并单元格，实现方案差别巨大，excel 需要保留空的单元格占位，而 html 不需要
     * <pre><code>
     * <mergeCells count="2">
     *   <mergeCell ref="A1:A2"/>
     *   <mergeCell ref="B3:B4"/>
     * </mergeCells>
     * </code></pre>
     *
     * @param address 矩形信息
     */
    public void mergeRegion(CellRangeAddress address) {
        // create empty cell in range
        for (int i = address.getFirstRow(), er = address.getLastRow(); i <= er; i++) {
            Row row = sheet.getRow(i);
            if (row == null) {
                row = sheet.createRow(i);
            }
            for (int j = address.getFirstColumn(), ec = address.getLastColumn(); j <= ec; j++) {
                // 注意：这里会重建第一个单元格，业务上可能会提前建好单元格，这对代码没什么影响。
                row.createCell(j);
            }
        }

        // merge cells in range
        sheet.addMergedRegion(address);
    }

    // getter/setter------------------------------------------------------------------------

    public int getStartRow() {
        return startRow;
    }

    public void setStartRow(int startRow) {
        this.startRow = startRow;
        this.rn = startRow;
    }

    public int getStartCol() {
        return startCol;
    }

    public void setStartCol(int startCol) {
        this.startCol = startCol;
        this.cn = startCol;
    }


    public Sheet getSheet() {
        return sheet;
    }

    public void setSheet(Sheet sheet) {
        this.sheet = sheet;
    }

    public Row getRow() {
        return row;
    }

    public void setRow(Row row) {
        this.row = row;
    }

    public Cell getCell() {
        return cell;
    }

    public void setCell(Cell cell) {
        this.cell = cell;
    }
}
